﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Permissions;


namespace TranslitMe
{
	public class TranslitMe
	{
		#region Настройки
		public Dictionary<char, Letter> Dict;
		public bool EncodeExtensions = false;
		public bool Subdirectories = true;
		public bool CopyDirectoriesTree = true;
		public string DestinationFolder  = "";
		public OperationType Operation = OperationType.Move;
		public WhenExistsAction WhenExists = WhenExistsAction.Rewrite;
		#endregion

		#region События

		public EventHandler<ResultArgs> FileRenamed;

		#endregion

		//TODO: рефакторинг класса
		[DllImport("kernel32.dll")]
		static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags);


		public TranslitMe()
		{
			Assembly a = Assembly.GetExecutingAssembly();
			LoadDict(a.GetManifestResourceStream("TranslitMe.dicts.СЭВ 1362-78.txt"));
		}

		//[PrincipalPermission(SecurityAction.Demand, Role=@"BUILTIN\Administrators")]
		private bool _createLink(string oldPath, string newPath)
		{
			return CreateSymbolicLink(newPath,oldPath , 0);
		}

		private string _getPath(string path, string basepath)
		{
			path = Path.GetFullPath(path);
			basepath = Path.GetFullPath(basepath);
			return Path.Combine(DestinationFolder, path.Substring(basepath.Length+1));
		}

		private void _fileRenamedEvent(string path, string newpath,ResultType type)
		{
			if (FileRenamed == null)
			{
				return;
			}

			ResultArgs args = new ResultArgs();
			args.NewFilepath = newpath;
			args.OldFilepath = path;
			args.Result = type;
			FileRenamed(this,args);
		}

		public struct Letter
		{
			public string Upper;
			public string Lower;
		}

		public class ResultArgs : EventArgs
		{
			public ResultType Result;
			public string OldFilepath;
			public string NewFilepath;
		}

		public enum ResultType
		{
			Skiped,
			Overwrited,
			Renamed,
			Moved
		}

		public enum OperationType
		{
			Move,
			Copy,
			SymbolicLink
		}

		public enum WhenExistsAction
		{
			Rewrite,
			Nothing,
			Rename
		}

		#region Методы словаря
		public void LoadDict(Stream stream)
		{
			StreamReader reader = new StreamReader(stream, Encoding.UTF8);
			Dict = new Dictionary<char, Letter>();
			while (!reader.EndOfStream)
			{
				string line = reader.ReadLine();
				if (line.Trim() == string.Empty)
				{
					continue;
				}

				string[] chars = line.Split(new char[] {'\t',' '}, StringSplitOptions.RemoveEmptyEntries);
				Letter letter = new Letter();
				letter.Upper = chars[1];
				letter.Lower = chars[2];
				Dict.Add(Char.Parse(chars[0]), letter);
			}
			reader.Close();

		}

		public void LoadDict(string filepath)
		{
			LoadDict(new FileStream(filepath, FileMode.Open, FileAccess.Read));
		}

		public void SaveDict(string filepath)
		{
			FileStream fs = new FileStream(filepath, FileMode.Create, FileAccess.Write);
			StreamWriter writer = new StreamWriter(fs, Encoding.UTF8);
			foreach (KeyValuePair<char, Letter> letter in Dict)
			{
				writer.WriteLine(letter.Key + "\t" + letter.Value.Lower + "\t" + letter.Value.Upper);
			}
			writer.Close();
		}

		public string DumpDict()
		{
			string result=String.Format("Dictionary ({0}):", Dict.Count);
			foreach (KeyValuePair<char,Letter> letter in Dict)
			{
				result += String.Format("\n“{0}”: {1} - {2}", letter.Key,letter.Value.Lower,letter.Value.Upper);
			}
			return result;
		}
		#endregion

		//TODO: переименовывание папок
		public void RenameFiles(string path)
		{
			SearchOption searchOption;
			if (Subdirectories)
			{
				searchOption = SearchOption.AllDirectories;
			}
			else
			{
				searchOption = SearchOption.TopDirectoryOnly;
			}
			string[] files = Directory.GetFiles(path, "*", searchOption);
			foreach (string file in files)
			{
				RenameFile(file, path);
			}
		}


		public void RenameFile(string filepath,string basepath)
		{
			string newpath;
			ResultType resultType = ResultType.Renamed;
			if (Operation == OperationType.Move)
			{
				resultType = ResultType.Moved;
			}

			filepath = Path.GetFullPath(filepath);

			if (DestinationFolder != string.Empty)
			{


				if (CopyDirectoriesTree)
				{
					newpath = _getPath(filepath, basepath);
				}
				else
				{
					newpath = Path.Combine(Path.GetFullPath(DestinationFolder), Path.GetFileName(filepath));
				}

				Directory.CreateDirectory(Path.GetDirectoryName(newpath));
			}
			else
			{
				newpath = Path.GetFullPath(filepath);
			}

			newpath = EncodeFilename(newpath);

			if (filepath != newpath)
			{
				
				if (File.Exists(newpath))
				{
					switch (WhenExists)
					{
						case WhenExistsAction.Rename:
							int i = 0;
							string newpath_renamed;
							string ext = Path.GetExtension(newpath);
							newpath = Path.Combine(Path.GetDirectoryName(newpath), Path.GetFileNameWithoutExtension(newpath));
							Console.WriteLine(newpath);
							do
							{
								newpath_renamed = newpath+"_"+i.ToString();
								newpath_renamed = Path.ChangeExtension(newpath_renamed, ext);
								i++;
							} while (File.Exists(newpath_renamed));
							newpath = newpath_renamed;
							break;

						case WhenExistsAction.Rewrite:
							File.Delete(newpath);
							resultType = ResultType.Overwrited;
							break;

						case WhenExistsAction.Nothing:
							resultType = ResultType.Skiped;
							break;
					}
				}

				if (!(File.Exists(newpath) && WhenExists==WhenExistsAction.Nothing))
				{
					switch (Operation)
					{
						case OperationType.SymbolicLink:
							_createLink(filepath, newpath);
							break;

						case OperationType.Move:
							File.Move(filepath, newpath);
							break;

						case OperationType.Copy:
							File.Copy(filepath, newpath);
							break;
					}
				}	
			}

			_fileRenamedEvent(filepath, newpath, resultType);
		}


		public string EncodeFilename(string filepath,bool encodeExt)
		{
			string filename = Encode(Path.GetFileName(filepath));
			string encodedFilepath = Path.GetDirectoryName(filepath) + "\\" + filename;

			if (!encodeExt)
			{
				encodedFilepath = Path.ChangeExtension(encodedFilepath, Path.GetExtension(filepath));
			}

			return Path.GetFullPath(encodedFilepath);
		}

		public string EncodeFilename(string filepath)
		{
			return EncodeFilename(filepath, EncodeExtensions);
		}

		public string Encode(string str)
		{
			char[] chars = str.ToCharArray();
			string result = "";
			
			foreach (char c in chars)
			{
				string strChar = c.ToString();
				string add = strChar;
				char chr = Char.Parse(strChar.ToLower());
				if (Dict.ContainsKey(chr))
				{
					if (strChar == strChar.ToLower())
					{
						add = Dict[chr].Lower;
					}
					else
					{
						add = Dict[chr].Upper;
					};
				}
				result += add;
			}
			return result;
		}
	}
}
